package UI;

import java.util.logging.Level;
import java.util.logging.Logger;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;

import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.border.Border;

import com.sun.org.apache.xerces.internal.impl.xpath.regex.ParseException;

import Task.DSTask;
import UI.Message.DSPopupMessage;
import UI.Table.DSTableCellRenderer;
import UI.Table.DSTaskDisplay;

//@author A0103535R
/**
 * DSUI is responsible for handling all the sub components that are related to
 * the drawing and display of components onto the screen.
 */
public class DSUI {
	private static final Logger _logger = Logger
			.getLogger(DSUI.class.getName());

	private static final int DELAY = 0;
	private static final int START_HEIGHT = 4;
	private static final int NO_EXCLUDE_INDEX = -1;

	private DSUIController _uiController;
	private JFrame _frame;
	private DSPanel _mainPanel;
	private JScrollPane _scrollPane;
	private JTable _table;
	private DSTaskDisplay _taskDisplay;
	private DSCommandLine _commandLine;
	private DSPopupMessage _popupMessage;
	private DSTableCellRenderer _renderer;
	private boolean _animationStatus;

	public DSUI(DSUIController uiController, Object[] rowData,
							boolean animationStatus, int backgroundNum) {
		_logger.setLevel(Level.OFF);
		_uiController = uiController;
		_animationStatus = animationStatus;
		initFrame();
		initTaskDisplay(rowData);
		initTableRenderer();
		initTable();
		initPanels(backgroundNum);
		initCommandLine();
		initPopupMessage();
		run();
	}
	
	//@author A0094762W
	public void updateUI(Object[] rowData) {
		updateTable(rowData);
		_renderer.updateCellMap(rowData);
		updateTableHeight(rowData, NO_EXCLUDE_INDEX);
	}

	public void addCommandUpdateUI(Object[] newTaskArray, DSTask task) {
		if (hasAnimation()) {
			addAnimationAndUpdateUI(newTaskArray, task);
		} else {
			updateUI(newTaskArray);
		}
	}

	public void deleteCommandUpdateUI(Object[] newTaskArray, DSTask task) {
		if (hasAnimation()) {
			deleteAnimationAndUpdateUI(newTaskArray, task);
		} else {
			updateUI(newTaskArray);
		}
	}

	public void expandTaskInUI(int expandStatus, DSTask task) {
		if (task == null) {
			expandOrHideAllTask(expandStatus);
		} else {
			_renderer.toggleSimplifiedStatus(task.getTaskId());
			_taskDisplay.refreshCell(expandStatus - 1);
			_renderer.updateCellMap(_taskDisplay.getTaskArray());
			_table.setRowHeight(expandStatus - 1,
					_renderer.getTableCellHeight(task.getTaskId()));
		}
	}

	public boolean setAnimationStatus() {
		_animationStatus = !_animationStatus;
		return _animationStatus;
	}

	public int changeBackground() {
		int backgroundNum = _mainPanel.changeBackground();
		
		return backgroundNum;
	}
	
	public DSTask getTaskFromTaskList(int index) {
		return _uiController.getTaskFromTaskList(index);
	}
	
	public int getTaskListSize() {
		return _uiController.getTaskListSize();
	}
	
	//@author A0103535R
	public void showMessage(String str) {
		Point origin = _frame.getLocationOnScreen();
		Dimension frame = _frame.getSize();
		_popupMessage.setLocation((int) origin.getX(), (int) origin.getY()
				+ frame.height);
		_popupMessage.showMessage(str);
	}

	public String obtainCommand() {
		return _commandLine.obtainCommand();
	}

	private void initFrame() {
		_frame = new JFrame();
		URL imageUrl = this.getClass().getClassLoader()
				.getResource("./icon.png");
		_frame.setTitle("DoSomething");
		_frame.setPreferredSize(new Dimension(DSUILayout.FRAME_SIZE_WIDTH,
				DSUILayout.FRAME_SIZE_HEIGHT));
		_frame.setMinimumSize(new Dimension(DSUILayout.FRAME_SIZE_WIDTH,
				DSUILayout.FRAME_SIZE_HEIGHT));
		_frame.setLocationRelativeTo(null);
		_frame.setResizable(false);
		_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

	}

	private void initPanels(int backgroundNum) throws ParseException {
		initMainPanel(backgroundNum);
		initScrollPanel();
	}

	private void initMainPanel(int backgroundNum) throws ParseException {
		_mainPanel = new DSPanel(backgroundNum);
		_mainPanel.setLayout(null);
		_mainPanel.setPreferredSize(new Dimension(DSUILayout.FRAME_SIZE_WIDTH,
				DSUILayout.FRAME_SIZE_HEIGHT));
		_mainPanel.setMaximumSize(new Dimension(DSUILayout.FRAME_SIZE_WIDTH,
				DSUILayout.FRAME_SIZE_HEIGHT));
		_frame.setContentPane(_mainPanel);
	}

	private void initScrollPanel() {
		_scrollPane = new JScrollPane(_table);
		_scrollPane.setOpaque(false);
		_scrollPane.getViewport().setOpaque(false);
		_scrollPane.setBorder(new Border() {
			@Override
			public boolean isBorderOpaque() {
				return true;
			}

			@Override
			public Insets getBorderInsets(Component c) {
				return new Insets(0, 0, 0, 0);
			}

			@Override
			public void paintBorder(Component c, Graphics g, int x, int y,
					int width, int height) {
			}
		});
	}

	private void initTaskDisplay(Object[] rowData) {
		_taskDisplay = new DSTaskDisplay(rowData);
	}

	private void initTableRenderer() {
		_renderer = new DSTableCellRenderer();
	}

	private void initTable() {
		_table = new JTable(_taskDisplay);
		_table.setDefaultRenderer(JPanel.class, _renderer);
		_table.setTableHeader(null);
		_table.setIntercellSpacing(new Dimension(0, 0));
		_table.setShowGrid(false);
		_table.setOpaque(false);
		_table.setBorder(new Border() {
			@Override
			public void paintBorder(Component c, Graphics g, int x, int y,
					int width, int height) {
			}

			@Override
			public boolean isBorderOpaque() {
				return false;
			}

			@Override
			public Insets getBorderInsets(Component c) {
				return new Insets(0, 0, 0, 0);
			}
		});
	}

	private void initCommandLine() {
		_logger.log(Level.INFO, "Entering initCommandLine");
		_commandLine = DSCommandLine.getInstance();
		_commandLine.setPreferredSize(new Dimension(
				DSUILayout.COMMAND_SIZE_WIDTH, DSUILayout.COMMAND_SIZE_HEIGHT));
		_commandLine.setMinimumSize(new Dimension(
				DSUILayout.COMMAND_SIZE_WIDTH, DSUILayout.COMMAND_SIZE_HEIGHT));
		_commandLine.addActionListener(_uiController);
		_commandLine.initialiseScrollPaneKeyBindings(_scrollPane);
		_logger.log(Level.INFO, "Exiting initCommandLine");
	}

	private void initPopupMessage() {
		_popupMessage = new DSPopupMessage(_frame);
	}

	private void run() {
		_mainPanel.setBounds(0, 0, DSUILayout.FRAME_SIZE_WIDTH,
				DSUILayout.FRAME_SIZE_HEIGHT);
		_scrollPane.setBounds(0, 0, DSUILayout.TABLE_SIZE_WIDTH,
				DSUILayout.TABLE_SIZE_WIDTH);
		_commandLine.setBounds(0, DSUILayout.TABLE_SIZE_WIDTH,
				DSUILayout.COMMAND_SIZE_WIDTH, DSUILayout.COMMAND_SIZE_HEIGHT);

		_mainPanel.add(_scrollPane);
		_mainPanel.add(_commandLine);
		_frame.pack();

		_commandLine.requestFocusInWindow();
		_frame.setVisible(true);
	}
	
	//@author A0094762W
	private void updateTable(Object[] newTaskArray) {
		_logger.log(Level.INFO, "Entering updateTable");

		_taskDisplay.setData(newTaskArray);

		_logger.log(Level.INFO, "Exiting updateTable");
	}

	private void updateTableHeight(Object[] newTaskArray, int excludeIndex) {
		for (int i = 0; i < newTaskArray.length; i++) {
			if (i != excludeIndex) {
				DSTask task = (DSTask) newTaskArray[i];
				String taskId = task.getTaskId();
				_table.setRowHeight(i, _renderer.getTableCellHeight(taskId));
			} else {
				_table.setRowHeight(i, START_HEIGHT);
			}
		}
	}

	private boolean hasAnimation() {
		return _animationStatus;
	}

	private int findIndexOfTask(DSTask task, Object[] taskArray) {
		for (int i = 0; i < taskArray.length; i++) {
			String currTaskId = ((DSTask) taskArray[i]).getTaskId();
			if (currTaskId.equals(task.getTaskId())) {
				return i;
			}
		}

		return -1;
	}

	private void addAnimationAndUpdateUI(Object[] newTaskArray, DSTask task) {
		final int taskIndex = findIndexOfTask(task, newTaskArray);
		_renderer.addNewTaskToCreateCell(task, taskIndex);

		updateTable(newTaskArray);
		updateTableHeight(newTaskArray, taskIndex);

		final String taskId = task.getTaskId();
		if (task != null && taskIndex >= 0) {
			(new Timer(DELAY, new ActionListener() {
				int height = START_HEIGHT;

				@Override
				public void actionPerformed(ActionEvent e) {
					if (height < _renderer.getTableCellHeight(taskId)) {
						_table.setRowHeight(taskIndex, height += 4);
					} else {
						((Timer) e.getSource()).stop();
					}
				}
			})).start();
		}
	}

	private void deleteAnimationAndUpdateUI(final Object[] newTaskArray,
			DSTask task) {
		Object[] oldTaskArray = _taskDisplay.getTaskArray();
		final int taskIndex = findIndexOfTask(task, oldTaskArray);
		final String taskId = task.getTaskId();

		(new Timer(DELAY, new ActionListener() {
			int height = _renderer.getTableCellHeight(taskId);

			@Override
			public void actionPerformed(ActionEvent e) {
				height -= 4;
				if (height > START_HEIGHT) {
					_table.setRowHeight(taskIndex, height);
				} else {
					((Timer) e.getSource()).stop();
					updateTable(newTaskArray);
					updateTableHeight(newTaskArray, NO_EXCLUDE_INDEX);
				}
			}
		})).start();
	}

	private void expandOrHideAllTask(int expandStatus) {
		if (expandStatus == -2) {
			_renderer.setAllSimplifiedStatus(true);
		} else if (expandStatus == -1) {
			_renderer.setAllSimplifiedStatus(false);
		}
		_taskDisplay.refreshTable();
		_renderer.updateCellMap(_taskDisplay.getTaskArray());
		updateTableHeight(_taskDisplay.getTaskArray(), NO_EXCLUDE_INDEX);
	}
}
